
class Attackable(object):
    """
    An object that can be attacked.
    """
    toHitRoll = property(lambda self: 4, doc="""
        Gives the die roll needed for a normal hit. Unusual hits are not handled
        here.
        """)
    def takeDamage(self, damage, dieRoll=None):
        """
        Tells the target how much damage a weapon thinks the target took, in addition to
        the die roll used to determine that.
        """
        raise NotImplementedError
    
class Attacker(object):
    """
    Something that can attack Attackable.
    """
    weaponPlots = {}
    hasFired = False
    inFirePhase = False
    
    def __init__(self):
        self.weaponPlots = {}
        self.hasFired = False
        self.inFirePhase = False
    
    def startAttackPhase(self):
        self.inFirePhase = True
    
    def stopAttackPhase(self):
        self.inFirePhase = False
        if len(self.weaponPlots) != 0:
            self.weaponPlots = {}
        
    def hasWeapon(self, weapon):
        """
        Returns True if this attacker has that weapon. Allows some default code in the base class.
        """
        raise NotImplementedError
    
    def plotAttack(self, target, weapon):
        """
        Used to tell an attacker that, when the signal is given, they will execute the described attack.
        Raises an exception if something goes wrong, eg the weapon given is already being used to attack.
        """
        if len(self.weaponPlots) == 0 and self.hasFired == False:
            self.hasFired = False
        
        if not self.hasWeapon(weapon):
            #raise an error
            return False
        if self.weaponPlots.has_key(weapon):
            #raise a different error
            return False
        
        self.weaponPlots[weapon] = target
        
    
    def doAttacks(self):
        """
        Do the already plotted attacks. If nothing plotted, do nothing.
        """
        if self.hasFired or not self.inFirePhase:
            return
        for weapon, target in self.weaponPlots:
            self.attack(target, weapon)
    
    def attack(self, target, weapon):
        """
        Used to figure out the results of attempting to attack a target using a
        given weapon.
        """
        raise NotImplementedError
    
    def parry(self, weapon, damage):
        """
        Used to figure out whether a character parries some weapons' damage. Weapon
        calls this after figuring out how much damage it would deal, if weapon can be
        parried (pistols, rifles, and cannons can't be parried).
        """
        raise NotImplementedError